package com.nado.extensions.util;

import java.util.Iterator;

public class ObjectName implements Iterable<ObjectName> {

	private static final char DELIMITER = '.';

	private final char delimiter;
	private final String fullname;

	public ObjectName(String fullname) {
		this.fullname = fullname;
		this.delimiter = DELIMITER;
	}

	public ObjectName(String fullname, char delimiter) {
		this.fullname = fullname;
		this.delimiter = delimiter;
	}

	public ObjectName(String fullname, String aliasName) {
		this.fullname = aliasName != null && !fullname.startsWith(aliasName) ? aliasName + DELIMITER + fullname : fullname;
		this.delimiter = DELIMITER;
	}

	public ObjectName(String fullname, String aliasName, char delimiter) {
		this.fullname = aliasName != null && !fullname.startsWith(aliasName) ? aliasName + delimiter + fullname : fullname;
		this.delimiter = delimiter;
	}

	public String name() {
		return fullname;
	}

	public String childName() {
		return childName(lastIndex(), fullname.length());
	}

	public int childNameAsIndex() {
		return Integer.valueOf(childName());
	}

	public String parentName() {
		return parentName(0, lastIndex());
	}

	public boolean isConditional() {
		return fullname.length() > 1 && fullname.charAt(fullname.length() - 1) == ']';
	}

	public boolean isIndexed() {
		for (char achar : childName().toCharArray()) {
			if (!Character.isDigit(achar)) {
				return false;
			}
		}
		return true;
	}

	public boolean isAttribute() {
		return childName().startsWith("@");
	}

	public boolean isLastName(String original) {
		return fullname.length() == original.length();
	}

	private String childName(int startIndex, int endIndex) {
		startIndex = startIndex < 0 ? 0 : startIndex + 1;
		endIndex = endIndex < 0 ? fullname.length() : endIndex < startIndex ? startIndex : endIndex;
		return startIndex == endIndex ? "" : fullname.substring(startIndex, fullname.charAt(endIndex - 1) == ']' ? endIndex - 1 : endIndex);
	}

	private String parentName(int startIndex, int endIndex) {
		startIndex = startIndex < 0 ? 0 : startIndex;
		endIndex = endIndex < 0 ? 0 : endIndex < startIndex ? startIndex : endIndex;
		return startIndex == endIndex ? "" : fullname.substring(startIndex, endIndex);
	}

	private int nextIndex(int index) {
		int dotIndex = fullname.indexOf(delimiter, index);
		int braceIndex = fullname.indexOf('[', index);
		return dotIndex < 0 && braceIndex < 0 ? index > fullname.length() ? -1 : fullname.length() :
				dotIndex >= 0 && braceIndex >= 0 ? Math.min(dotIndex, braceIndex) :
						dotIndex >= 0 ? dotIndex : braceIndex;
	}

	private int lastIndex() {
		int dotIndex = fullname.lastIndexOf(delimiter);
		int braceIndex = fullname.lastIndexOf('[');
		return dotIndex < 0 && braceIndex < 0 ? -1 :
				dotIndex >= 0 && braceIndex >= 0 ? Math.max(dotIndex, braceIndex) :
						dotIndex >= 0 ? dotIndex : braceIndex;
	}

	public ObjectName[] split(int nth) {
		int index = 0;
		for (int i = 0; i < nth; i++) {
			index = nextIndex(index + 1);
		}
		return new ObjectName[]{
				new ObjectName(fullname.substring(0, index), delimiter),
				new ObjectName(index >= fullname.length() ? "" : fullname.substring(fullname.charAt(index) == delimiter ? index + 1 : index), delimiter)
		};
	}

	public int hashCode() {
		return fullname.hashCode();
	}

	public boolean equals(Object object) {
		return fullname.equals(((ObjectName) object).fullname);
	}

	public String toString() {
		return fullname;
	}

	public Iterator<ObjectName> iterator() {
		return new FullIterator();
	}

	public Iterator<ObjectName> childIterator() {
		return new FullIterator() {
			public boolean hasNext() {
				return super.hasNext() && index < fullname.length();
			}
		};
	}

	protected class FullIterator implements Iterator<ObjectName> {

		protected int index;

		public boolean hasNext() {
			return index >= 0 && (index = nextIndex(index + 1)) >= 0;
		}

		public ObjectName next() {
			return new ObjectName(fullname.substring(0, index), delimiter);
		}

		public void remove() {
			throw new UnsupportedOperationException();
		}
	}
}
